/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Game;

import java.awt.*;
import java.io.*;
import java.net.URL;
import java.util.ArrayList;
import java.awt.image.BufferedImage;
import javax.imageio.*;
import java.awt.*;
import java.awt.geom.AffineTransform;
import java.io.*;
import java.util.ArrayList;
import javax.swing.ImageIcon;
import java.net.URL;
import java.net.MalformedURLException;
import java.awt.image.ColorConvertOp;
import java.awt.color.ColorSpace;

import util.*;
import graphics.*;
import Game.sprites.*;
import sound.*;


/**
 *
 * @author Yati
 */
public class TilegameResourceManager extends util.ResourceManager{
    private Image[][] tileFadeBank; 
    private final int fadeFrameNumber=10;
    private final long fadeDur=1000;
    private final long fadeFrameDur=fadeDur/fadeFrameNumber;
    //ArrayList of colored tile images
    private ArrayList colorTiles;
    //ArrayList of greyscaled tile images
    private ArrayList greyTiles;
    private int currentMap;

    // host sprites used for cloning
    private Sprite playerSprite;
    private Sprite musicSprite;
    private Sprite coinSprite;
    private Sprite goalSprite;
    private Sprite grubSprite;
    private Sprite flySprite;
    
    /**
        Creates a new ResourceManager with the specified
        GraphicsConfiguration.
    */
    public TilegameResourceManager(GraphicsConfiguration gc,SoundManager soundManager,MidiPlayer midiPlayer)
    {
        super(gc, soundManager, midiPlayer);
    }
    
    public void loadResources() {
        loadTileImages();
        loadCreatureSprites();
        loadPowerUpSprites();
        loadFadeImages();
    }
    
    //-------------------
    //LOADING RESOURCES
    //-------------------
    
    public void loadTileImages() {
        // keep looking for tile A,B,C, etc. this makes it
        // easy to drop new tiles in the images/ directory

        colorTiles=new ArrayList();
        greyTiles=new ArrayList();
        char ch = 'A';
        while (true) {
            String name = "tile_" + ch + ".png";
            File file = new File("images/" + name);
            if (!file.exists()) {
                break;
            }
            //tiles.add(loadMaskedImage(name));
            //add colored tile to colorTiles
            colorTiles.add(loadMaskedImage(name));
            URL path=null;
            path=getClass().getResource("/images/"+name);
                //add greyscaled image to greyTiles
                greyTiles.add(makeGrayScale(path,true));
                ch++;
        }
    }
    
    public void loadFadeImages(){
        tileFadeBank=new Image[greyTiles.size()][fadeFrameNumber];
        float rate=1.0f/fadeFrameNumber;
        for(int tile=0;tile<greyTiles.size();tile++){
            float alpha=1.0f;
            for(int frame=0;frame<fadeFrameNumber;frame++){
                BufferedImage fadeFrom=convertImageToBuffered((Image)greyTiles.get(tile));
                BufferedImage fadeTo=convertImageToBuffered((Image)colorTiles.get(tile));

                //System.out.println("Alpha: "+alpha);
                BufferedImage newFrame=new BufferedImage(fadeFrom.getWidth(),fadeFrom.getHeight(),BufferedImage.TYPE_INT_ARGB);
                //alpha=1.0f*i/noOfFrames;
                Graphics2D g= newFrame.createGraphics();


                g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,alpha));
                //g.setComposite(AlphaComposite.Src);
                g.drawImage(fadeFrom, null, 0,0);

                g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR,1-alpha));  
                g.drawImage(fadeTo, null, 0,0);

                g.dispose();
                tileFadeBank[tile][frame]=Toolkit.getDefaultToolkit().createImage(newFrame.getSource());
                alpha=alpha-rate;
            }
            
        }
    }
    
    public void loadCreatureSprites() {

        Image[][] images = new Image[4][];

        // load left-facing images
        images[0] = new Image[] {
            loadMaskedImage("0001.png"),
            loadMaskedImage("0002.png"),
            loadMaskedImage("0003.png"),
            loadMaskedImage("0004.png"),
            loadMaskedImage("0005.png"),
            loadMaskedImage("0006.png"),
            loadImage("fly1.png"),
            loadImage("fly2.png"),
            loadImage("fly3.png"),
            loadImage("grub1.png"),
            loadImage("grub2.png"),
        };

        images[1] = new Image[images[0].length];
        images[2] = new Image[images[0].length];
        images[3] = new Image[images[0].length];
        for (int i=0; i<images[0].length; i++) {
            // right-facing images
            images[1][i] = getMirrorImage(images[0][i]);
            // left-facing "dead" images
            images[2][i] = getFlippedImage(images[0][i]);
            // right-facing "dead" images
            images[3][i] = getFlippedImage(images[1][i]);
        }

        // create creature animations
        Animation[] playerAnim = new Animation[4];
        Animation[] flyAnim = new Animation[4];
        Animation[] grubAnim = new Animation[4];
        for (int i=0; i<4; i++) {
            playerAnim[i] = createPlayerAnim(
                images[i][0], images[i][1], images[i][2],images[i][3],images[i][4],images[i][5]);
            flyAnim[i] = createFlyAnim(
                images[i][6], images[i][7], images[i][8]);
            grubAnim[i] = createGrubAnim(
                images[i][9], images[i][10]);
        }

        // create creature sprites
        playerSprite = new Player(playerAnim[0], playerAnim[1],
            playerAnim[2], playerAnim[3]);
        flySprite = new Fly(flyAnim[0], flyAnim[1],
            flyAnim[2], flyAnim[3]);
        grubSprite = new Grub(grubAnim[0], grubAnim[1],
            grubAnim[2], grubAnim[3]);
    }
        
    private void loadPowerUpSprites() {
        // create "goal" sprite
        Animation anim = new Animation();
        anim.addFrame(loadImage("heart1.png"), 150);
        anim.addFrame(loadImage("heart2.png"), 150);
        anim.addFrame(loadImage("heart3.png"), 150);
        anim.addFrame(loadImage("heart2.png"), 150);
        goalSprite = new PowerUp.Goal(anim);

        // create "star" sprite
        anim = new Animation();
        anim.addFrame(loadImage("star1.png"), 100);
        anim.addFrame(loadImage("star2.png"), 100);
        anim.addFrame(loadImage("star3.png"), 100);
        anim.addFrame(loadImage("star4.png"), 100);
        coinSprite = new PowerUp.Star(anim);

        // create "music" sprite
        anim = new Animation();
        anim.addFrame(loadImage("powerup1.png"), 150);
        anim.addFrame(loadImage("powerup2.png"), 150);
        anim.addFrame(loadImage("powerup3.png"), 150);
        anim.addFrame(loadImage("powerup2.png"), 150);
        musicSprite=new PowerUp.ColorSwitch(anim); 
        //!!!!!!!
        //FOR TESTING THE MUSIC SPRITE IS NOW FADE TEST
        //!!!!!!!
        //musicSprite = new PowerUp.Music(anim);
        //int tile = 'B' - 'A';
        //musicSprite = new PowerUp.Music(createFade((Image)greyTiles.get(tile),(Image)colorTiles.get(tile),3000,1000));
    }
    
    //---------------------------
    // UTILITY METHODS
    //--------------------------
    public Image makeGrayScale(URL path,boolean masked){
        ColorConvertOp grayScaleFilter= new ColorConvertOp(ColorSpace.getInstance(ColorSpace.CS_GRAY), null);
        BufferedImage original=null;
        Image grayScaleImage=null;


        if(masked)
            original=loadBufferedMasked(path,Color.red);
        else{
            try{
                original=ImageIO.read(path);
            }catch(IOException e){
                e.printStackTrace();
            }
        }


        if(original!=null){
            grayScaleImage=grayScaleFilter.filter(original, null);

        }

        //System.out.println("Color dimensions: "+original.getHeight()+", "+original.getWidth());
        //System.out.println("Gray Dimensions: "+grayScaleImage.getHeight(null)+", "+grayScaleImage.getWidth(null));

        return grayScaleImage;
    }
    
    public BufferedImage loadBufferedMasked(URL path, Color color){
        BufferedImage image=null;
        //String dir=System.getProperty("user.dir");
        //URL url=getClass().getResource(dir+"/"+path);

        //URL test=getClass().getResource("images/0001.png");

        /*if(test==null){
            System.out.print("hi yo");
        }*/


        try{
            image=ImageIO.read(path);

        }catch(IOException e){
            System.err.println("This is the error");
            e.printStackTrace();
        }

        BufferedImage dimg= new BufferedImage(image.getWidth(),image.getHeight(),BufferedImage.TYPE_INT_ARGB);

        Graphics2D g= dimg.createGraphics();
        g.setComposite(AlphaComposite.Src);
        g.drawImage(image, null, 0,0);
        g.dispose();
        for(int i=0;i<dimg.getHeight();i++){
            for(int j=0;j<dimg.getWidth();j++){
                if(dimg.getRGB(j, i)==color.getRGB()){
                    dimg.setRGB(j, i, 0x8F1C1C);
                }
            }
        }
        return dimg;
    }
    
    public Image loadMaskedImage(String path){
        URL filePath=getClass().getResource("/images/"+path);
        return makeMasked(filePath,Color.red);
    }
    
    private Animation createPlayerAnim(Image player1,
        Image player2, Image player3, Image player4, Image player5, Image player6)
    {
        Animation anim = new Animation();
        anim.addFrame(player1, 100);
        anim.addFrame(player2, 100);
        anim.addFrame(player1, 100);
        anim.addFrame(player2, 100);
        anim.addFrame(player3, 100);
        anim.addFrame(player2, 100);
        return anim;
    }


    private Animation createFlyAnim(Image img1, Image img2,
        Image img3)
    {
        Animation anim = new Animation();
        anim.addFrame(img1, 50);
        anim.addFrame(img2, 50);
        anim.addFrame(img3, 50);
        anim.addFrame(img2, 50);
        return anim;
    }


    private Animation createGrubAnim(Image img1, Image img2) {
        Animation anim = new Animation();
        anim.addFrame(img1, 250);
        anim.addFrame(img2, 250);
        return anim;
    }
    
    private Tile createTile(int tileIndex,char ch,boolean collidable){
        Animation anim1=new Animation();
        for(int i=0;i<fadeFrameNumber;i++){
            anim1.addFrame(tileFadeBank[tileIndex][i], fadeFrameDur);
        }
        
        Animation anim2=new Animation();
        for(int i=fadeFrameNumber-1;i>=0;i--){
            anim2.addFrame(tileFadeBank[tileIndex][i], fadeFrameDur);
        }
        anim1.setLooping(false);
        anim2.setLooping(false);
        return new Tile(anim1,anim2,ch,collidable);
    }
    
    public Animation createFade(Image image1,Image image2,long totalDuration,int noOfFrames){
        BufferedImage fadeFrom=convertImageToBuffered(image1);
        BufferedImage fadeTo=convertImageToBuffered(image2);
        Animation newAnim=new Animation();
        //============
        //Technique 1
        //============
        /*for(int i=0;i<noOfFrames;i++){
            BufferedImage newFrame=new BufferedImage(fadeFrom.getWidth(),fadeFrom.getHeight(),BufferedImage.TYPE_INT_ARGB);
            float alpha=1.0f*i/noOfFrames;
            Graphics2D g= newFrame.createGraphics();
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR,0.3f));
            g.drawImage(fadeTo, null, 0,0);
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR, 1-alpha));
            g.drawImage(fadeFrom, null, 0,0);
            g.dispose();
            newAnim.addFrame(Toolkit.getDefaultToolkit().createImage(newFrame.getSource()),totalDuration/noOfFrames);
        }*/
        //============
        //Technique 2
        //============
        int i=0;
        float rate=1.0f/noOfFrames;
        //float alpha=1.0f*i/noOfFrames;
        float alpha=1.0f;
        //System.out.println("New loop started! The rate is: "+rate);
        while(alpha>0){
            //System.out.println("Alpha: "+alpha);
            BufferedImage newFrame=new BufferedImage(fadeFrom.getWidth(),fadeFrom.getHeight(),BufferedImage.TYPE_INT_ARGB);
            //alpha=1.0f*i/noOfFrames;
            Graphics2D g= newFrame.createGraphics();
           
            
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC,alpha));
            //g.setComposite(AlphaComposite.Src);
            g.drawImage(fadeFrom, null, 0,0);
                        
            g.setComposite(AlphaComposite.getInstance(AlphaComposite.XOR,1-alpha));  
            g.drawImage(fadeTo, null, 0,0);

            g.dispose();
            newAnim.addFrame(Toolkit.getDefaultToolkit().createImage(newFrame.getSource()),totalDuration/noOfFrames);
            alpha=alpha-rate;
            /*if(alpha<0){
            
                
            }*/
            //i++;
            //System.out.println("Alpha(after): "+alpha);
        }
        
        //============
        //Technique 3 INCOMPLETE
        //============
        /*float alpha=1.0f;
        float decrease=totalDuration/noOfFrames;
        while(alpha>0){
            BufferedImage newFrame=new BufferedImage(fadeFrom.getWidth(),fadeFrom.getHeight(),BufferedImage.TYPE_INT_ARGB);
            Graphics2D g= newFrame.createGraphics();
            alpha-=decrease;

            for(int i=0;i<newFrame.getHeight();i++){
                for(int j=0;j<newFrame.getWidth();j++){
                    newFrame.setRGB(j, i, combine(Color(fadeFrom.getRGB(j, i)),fadeTo.getRGB(j, i),alpha));
                }
            }
        }*/

        return newAnim;
    }
    
    //-----------------------------
    //MAP LOADING METHODS
    //-----------------------------
    
    public TileMap loadNextMap() {
        TileMap map = null;
        while (map == null) {
            currentMap++;
            try {
                map = loadMap(
                    "maps/map" + currentMap + ".txt");
            }
            catch (IOException ex) {
                if (currentMap == 1) {
                    // no maps to load!
                    return null;
                }
                currentMap = 0;
                map = null;
            }
        }

        return map;
    }


    public TileMap reloadMap() {
        try {
            return loadMap(
                "maps/map" + currentMap + ".txt");
        }
        catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }


    private TileMap loadMap(String filename)
        throws IOException
    {
        ArrayList lines = new ArrayList();
        int width = 0;
        int height = 0;

        // read every line in the text file into the list
        BufferedReader reader = new BufferedReader(
            new FileReader(filename));
        while (true) {
            String line = reader.readLine();
            // no more lines to read
            if (line == null) {
                reader.close();
                break;
            }

            // add every line except for comments
            if (!line.startsWith("#")) {
                lines.add(line);
                width = Math.max(width, line.length());
            }
        }

        // parse the lines to create a TileEngine
        height = lines.size();
        TileMap newMap = new TileMap(width, height);
        for (int y=0; y<height; y++) {
            String line = (String)lines.get(y);
            for (int x=0; x<line.length(); x++) {
                char ch = line.charAt(x);

                // check if the char represents tile A, B, C etc.
                int tile = ch - 'A';
                if (tile >= 0 && tile < greyTiles.size()) {
                    //newMap.setTile(x, y, (Image)tiles.get(tile));
                    
                    //The animation for fading
                    Animation anim=createFade((Image)greyTiles.get(tile),(Image)colorTiles.get(tile),1000,10);
                    Animation revAnim=createFade((Image)colorTiles.get(tile),(Image)greyTiles.get(tile),1000,10);
                    anim.setLooping(false);
                    revAnim.setLooping(false);
                    if(ch!='J')
                        newMap.setTile(x, y, createTile(tile, ch, true));
                        //newMap.setTile(x, y,new Tile((Image)greyTiles.get(tile),anim, ch, true));
                        //newMap.setTile(x, y,new Tile(anim,revAnim, ch, true));
                    else
                        newMap.setTile(x, y, createTile(tile, ch, false));
                        //newMap.setTile(x, y,new Tile((Image)greyTiles.get(tile),anim, ch, false));
                        //newMap.setTile(x, y,new Tile(anim,revAnim, ch, false));
                }
                //load the specified background image
                else if(ch == '/'){
                    System.out.println("found background data");
                    String background="images/"+line.substring(1);
                    URL path=getClass().getResource("/"+background);
                    Image backGS=makeGrayScale(path,false);
                    Image backC=loadImage(line.substring(1));
                    //newMap.setBackground(loadImage(background));
                    Animation anim1=createFade(backGS, backC, 1000, 10);
                    Animation anim2=createFade(backC, backGS, 1000, 10);
                    anim1.setLooping(false);
                    anim2.setLooping(false);
                    newMap.initialize(anim1, anim2);
                    //newMap.setBackgroundC(backC);
                    //newMap.setBackgroundGS(backGS);
                    //newMap.setBackground(backGS);
                }
                // check if the char represents a sprite
                else if (ch == 'o') {
                    addSprite(newMap, coinSprite, x, y);
                }
                else if (ch == '!') {
                    addSprite(newMap, musicSprite, x, y);
                }
                else if (ch == '*') {
                    addSprite(newMap, goalSprite, x, y);
                }
                else if (ch == '1') {
                    addSprite(newMap, grubSprite, x, y);
                }
                else if (ch == '2') {
                    addSprite(newMap, flySprite, x, y);
                }
            }
        }

        // add the player to the map
        Sprite player = (Sprite)playerSprite.clone();
        player.setX(TileMapRenderer.tilesToPixels(3));
        player.setY(0);
        newMap.setPlayer(player);
        return newMap;
    }


    private void addSprite(TileMap map,
        Sprite hostSprite, int tileX, int tileY)
    {
        if (hostSprite != null) {
            // clone the sprite from the "host"
            Sprite sprite = (Sprite)hostSprite.clone();

            // center the sprite
            sprite.setX(
                TileMapRenderer.tilesToPixels(tileX) +
                (TileMapRenderer.tilesToPixels(1) -
                sprite.getWidth()) / 2);

            // bottom-justify the sprite
            sprite.setY(
                TileMapRenderer.tilesToPixels(tileY + 1) -
                sprite.getHeight());

            // add it to the map
            map.addSprite(sprite);
        }
    }
    
    public Image getColoredTile(char ch){
        int tile = ch - 'A';
        return (Image)colorTiles.get(tile);
    }

    



}
